package demo;

import com.soon.ws.utils.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.security.MessageDigest;

public class EncryptionAlgorithm {

    private static final Logger logger = LoggerFactory.getLogger(EncryptionAlgorithm.class);

    private static final String KEY_MD5 = "MD5";

    private static final String KEY_SHA = "SHA";

    private static final String KEY_MAC = "HmacMD5";

    public static void main(String[] args) {

        String str = "1234321~";

        EncryptionAlgorithm ea = new EncryptionAlgorithm();
        try {
            // 实例化一个静态内部类
            Base64 base64 = new EncryptionAlgorithm.Base64();
            // 加密
            String res1 = base64.encryptBASE64(str.getBytes());
            logger.info("【base64加密后】={}", res1);

            // 解密
            byte _res[] = base64.decryptBASE64(res1);
            String res2 = new String(_res);
            logger.info("【base64解密后】={}", res2);
        } catch (Exception e) {
            logger.error("【异常】={}", e);
        }

        System.out.println("\n\n\n");

        // 实例化一个内部类
        Md5 md5 = ea.new Md5();
        try {
            logger.info("【md5加密后】={}", md5.getResult(str));
            ;
        } catch (Exception e) {
            logger.error("【异常】={}", e);
        }

        System.out.println("\n\n\n");

        try {
            SHA sha = ea.new SHA();
            logger.info("【sha加密后】={}", sha.getResult(str));
        } catch (Exception e) {
            logger.error("【异常】={}", e);
        }

        System.out.println("\n\n\n");

        try {
            /*使用同一密钥：对数据进行加密：查看两次加密的结果是否一样*/
            EncryptionAlgorithm.HMAC.getResult1(str);
            EncryptionAlgorithm.HMAC.getResult2(str);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * Base64加解密
     * BASE64的加密解密是双向的，可以求反解。
     */
    static class Base64 {

        /**
         * BASE64解密
         *
         * @param key
         * @return
         * @throws Exception
         */
        private static byte[] decryptBASE64(String key) throws Exception {
            return (new BASE64Decoder()).decodeBuffer(key);
        }

        /**
         * BASE64加密
         *
         * @param key
         * @return
         * @throws Exception
         */
        private static String encryptBASE64(byte[] key) throws Exception {
            return (new BASE64Encoder()).encode(key);
        }

    }

    /**
     * MD5即Message-Digest Algorithm 5（信息-摘要算法5），
     * 用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一（又译摘要算法、哈希算法），
     * 主流编程语言普遍已有MD5实现。将数 据（如汉字）运算为另一固定长度值，
     * 是杂凑算法的基础原理，MD5的前身有MD2、MD3和 MD4。
     * 广泛用于加密和解密技术，常用于文件校验。
     * 校验？不管文件多大，经过MD5后都能生成唯一的MD5值。
     * 好比现在的ISO校验，都是MD5校验。
     * 怎么用？当然是把ISO经过MD5后产生MD5的值。
     * 一般下载linux-ISO的朋友都见过下载链接旁边放着MD5的串。
     * 就是用来验证文件是否一致的。
     */
    class Md5 {

        /**
         * 加密
         *
         * @param inputStr
         * @return
         */
        private String getResult(String inputStr) throws Exception {
            logger.info("【md5加密前】={}", inputStr);
            BigInteger bigInteger = null;
            MessageDigest md = MessageDigest.getInstance(KEY_MD5);
            byte inputData[] = inputStr.getBytes();
            md.update(inputData);
            bigInteger = new BigInteger(md.digest());
            return bigInteger.toString(16);
        }
        /**
         * MD5算法具有以下特点：
         * 1、压缩性：任意长度的数据，算出的MD5值长度都是固定的。
         * 2、容易计算：从原数据计算出MD5值很容易。
         * 3、抗修改性：对原数据进行任何改动，哪怕只修改1个字节，所得到的MD5值都有很大区别。
         * 4、弱抗碰撞：已知原数据和其MD5值，想找到一个具有相同MD5值的数据（即伪造数据）是非常困难的。
         * 5、强抗碰撞：想找到两个不同的数据，使它们具有相同的MD5值，是非常困难的。
         * MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被”压缩”成一种保密的格式
         * （就是把一个任意长度的字节串变换成一定长的十六进制数字串）。
         * 除了MD5以外，其中比较有名的还有sha-1、RIPEMD以及Haval等。
         */
    }

    /**
     * 安全散列算法（Secure Hash Algorithm）
     * 主要适用于数字签名标准（Digital Signature Standard DSS）
     * 里面定义的数字签名算法（Digital Signature Algorithm DSA）。
     * 对于长度小于2^64位的消息，SHA1会产生一个160位的消息摘要。
     * 该算法经过加密专家多年来的发展和改进已日益完善，并被广泛使用。
     * 该算法的思想是接收一段明文，然后以一种不可逆的方式将它转换成一段（通常更小）密文，
     * 也可以简单的理解为取一串输入码（称为预映射或信息），
     * 并把它们转化为 长度较短、位数固定的输出序列即散列值（也称为信息摘要或信息认证代码）的过程。
     * 散列函数值可以说是对明文的一种“指纹”或是“摘要”，
     * 所以对散列值的数字签名就可以视为对此明文的数字签名。
     */
    class SHA {

        /**
         * SHA(Secure Hash Algorithm，安全散列算法），数字签名等密码学应用中重要的工具，
         * 被广泛地应用于电子商务等信息安全领域。虽然，SHA与MD5通过碰撞法都被破解了，
         * 但是SHA仍然是公认的安全加密算法，较之MD5更为安全
         */
        private String getResult(String inputStr) throws Exception {
            logger.info("【sha加密前】={}", inputStr);
            BigInteger bigInteger = null;
            byte inputData[] = inputStr.getBytes();
            MessageDigest md = MessageDigest.getInstance(KEY_SHA);
            md.update(inputData);
            bigInteger = new BigInteger(md.digest());
            return bigInteger.toString(32);
        }

    }

    /**
     * HMAC(Hash Message Authentication Code，散列消息鉴别码，
     * 基于密钥的Hash算法的认证协议。消息鉴别码实现鉴别的原理是，
     * 用公开函数和密钥产生一个固定长度的值作为认证标识，用这个标识鉴别消息的完整性。
     * 使用一个密钥生成一个固定大小的小数据块，即MAC，并将其加入到消息中，然后传输。
     * 接收方利用与发送方共享的密钥进行鉴别认证等。
     */
    public static class HMAC {

        /**
         * 初始化HMAC密钥
         *
         * @return
         * @throws Exception
         */
        private static String initMacKey() throws Exception {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_MAC);
            SecretKey secretKey = keyGenerator.generateKey();
            return EncryptionAlgorithm.Base64.encryptBASE64(secretKey.getEncoded());
        }

        /**
         * HMAC加密，主要方法
         *
         * @param data
         * @param key
         * @return
         * @throws Exception
         */
        private static String encryptHMAC(byte[] data, String key) throws Exception {
            SecretKey secretKey = new SecretKeySpec(EncryptionAlgorithm.Base64.decryptBASE64(key), KEY_MAC);
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
            return new String(mac.doFinal(data));
        }

        private static String getResult1(String inputStr) {
            String path = EncryptionAlgorithm.class.getResource("/").getPath();
            String fileSource = path + "/file/";
            String result = null;
            try {
                byte[] inputData = inputStr.getBytes();
                /*产生密钥*/
                String key = initMacKey();
                System.out.println("MAC秘钥-->" + key);
                /*将密钥写文件*/
                FileUtil.writeContentFile(key, fileSource, "HMAC_key.txt", false);
                /*对数据进行加密*/
                result = HMAC.encryptHMAC(inputData, key);
                logger.info("【HMAC加密1后：】={}", result);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }

        private static String getResult2(String inputStr) {
            String path = EncryptionAlgorithm.class.getResource("/").getPath();
            String fileSource = path + "/file/HMAC_key.txt";
            String key = null;
            /*将密钥从文件中读取*/
            key = FileUtil.readFile(fileSource);
            System.out.println("getResult2密钥-->" + key);

            String result = null;
            try {
                byte[] inputData = inputStr.getBytes();
                /*对数据进行加密*/
                result = HMAC.encryptHMAC(inputData, key);
                logger.info("【HMAC加密2后：】={}", result);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }

}
